// 版权所有2012 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件实现转换的类型检查。

package types

import (
	"go/constant"
	"unicode"
)

// 转换类型检查转换T（x）。
// 结果在x中。
func (check *Checker) conversion(x *operand, T Type) {
	constArg := x.mode == constant_

	constConvertibleTo := func(T Type, val *constant.Value) bool {
		switch t, _ := under(T).(*Basic); {
		case t == nil:
			// 无需任何操作
		case representableConst(x.val, check, t, val):
			return true
		case isInteger(x.typ) && isString(t):
			codepoint := unicode.ReplacementChar
			if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune {
				codepoint = rune(i)
			}
			if val != nil {
				*val = constant.MakeString(string(codepoint))
			}
			return true
		}
		return false
	}

	var ok bool
	var cause string
	switch {
	case constArg && isConstType(T):
		// 常数转换
		ok = constConvertibleTo(T, &x.val)
	case constArg && isTypeParam(T):
		// 如果x可转换
		// 到T的类型集中的每个特定类型。
		// 如果T的类型集为空，或者如果它不具有特定类型，常数x不能被
		// 转换。
		ok = T.(*TypeParam).underIs(func(u Type) bool {
			// 如果没有特定的类型项，则u为零
			if u == nil {
				cause = check.sprintf("%s does not contain specific types", T)
				return false
			}
			if isString(x.typ) && isBytesOrRunes(u) {
				return true
			}
			if !constConvertibleTo(u, nil) {
				cause = check.sprintf("cannot convert %s to %s (in %s)", x, u, T)
				return false
			}
			return true
		})
		x.mode = value // 类型参数不是常量
	case x.convertibleTo(check, T, &cause):
		// 非常量转换
		ok = true
		x.mode = value
	}

	if !ok {
		// TODO（rfindley）：在此处使用types2样式的错误报告。
		if compilerErrorMessages {
			if cause != "" {
				// 如果我们有以下原因，请在行尾添加冒号。
				check.errorf(x, _InvalidConversion, "cannot convert %s to type %s:\n\t%s", x, T, cause)
			} else {
				check.errorf(x, _InvalidConversion, "cannot convert %s to type %s", x, T)
			}
		} else {
			if cause != "" {
				check.errorf(x, _InvalidConversion, "cannot convert %s to %s (%s)", x, T, cause)
			} else {
				check.errorf(x, _InvalidConversion, "cannot convert %s to %s", x, T)
			}
		}
		x.mode = invalid
		return
	}

	// 转换参数类型是最终的。对于非类型化的值，根据规范，
	// 转换提供类型：“常数可以是
	// 通过常数声明或转换显式给定的类型，…”。
	if isUntyped(x.typ) {
		final := T
		// /-对于到接口的转换，请使用参数的默认类型。
		// -对于非类型化常量到非常量类型的转换，也可以使用默认类型（例如，[]字节（“foo”）应报告字符串
		// not[]字节作为常量“foo”的类型）。
		// /-对于非类型的nil参数，请保留非类型的nil。
		// /-对于常量整数到字符串的转换，保留参数类型。
		// （另请参见下面的待办事项。）
		if IsInterface(T) && !isTypeParam(T) || constArg && !isConstType(T) || x.isNil() {
			final = Default(x.typ) // 非类型化nil的默认类型为非类型化nil 
		} else if x.mode == constant_ && isInteger(x.typ) && allString(T) {
			final = x.typ
		}
		check.updateExprType(x.expr, final, true)
	}

	x.typ = T
}

// TODO（gri）CONVERTIBLE检查T（x）是否有效。它假设x的
// 类型是完全已知的，但say string（1<<s+1.0）的情况并非如此：
// 这里，1<<s+1.0的类型将是非类型的float，这将导致
// （正确！）拒绝转换。但报告的错误本质上是
// “无法将非类型化的浮点值转换为字符串”，但正确的错误（根据
// 规范）是我们无法转换浮点值：1/1应
// 转换为非类型化的浮点值，因为添加了1.0。修复这个
// 很棘手，因为我们必须首先对参数运行UpdateExprtType。
// （第21982期）

// convertibleTo报告T（x）是否有效。在故障情况下，可将*cause 
// 设置为故障原因。
// 如果通过
// 导出的API调用调用convertibleTo，则check参数可能为零，即当所有方法都已进行类型检查时。
func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
	// “x可分配给T”
	if ok, _ := x.assignableTo(check, T, cause); ok {
		return true
	}

	// “如果忽略标记，则V和T具有相同的基础类型
	// ，且V和T不是类型参数”
	V := x.typ
	Vu := under(V)
	Tu := under(T)
	Vp, _ := V.(*TypeParam)
	Tp, _ := T.(*TypeParam)
	if IdenticalIgnoreTags(Vu, Tu) && Vp == nil && Tp == nil {
		return true
	}

	// “V和T是未命名的指针类型，它们的指针基类型
	// 如果忽略标记
	// 它们的指针基类型不是类型参数”
	if V, ok := V.(*Pointer); ok {
		if T, ok := T.(*Pointer); ok {
			if IdenticalIgnoreTags(under(V.base), under(T.base)) && !isTypeParam(V.base) && !isTypeParam(T.base) {
				return true
			}
		}
	}

	// “V和T都是整数或浮点类型”
	if isIntegerOrFloat(Vu) && isIntegerOrFloat(Tu) {
		return true
	}

	// “V和T都是复杂类型”
	if isComplex(Vu) && isComplex(Tu) {
		return true
	}

	// ”“V是一个整数或字节或符码片，T是字符串类型”
	if (isInteger(Vu) || isBytesOrRunes(Vu)) && isString(Tu) {
		return true
	}

	// “V是一个字符串，T是字节或符码片”
	if isString(Vu) && isBytesOrRunes(Tu) {
		return true
	}

	// package unsafe:
	// “基础类型uintptr的任何指针或值都可以转换为unsafe.pointer”
	if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(Tu) {
		return true
	}
	// “
	if isUnsafePointer(Vu) && (isPointer(Tu) || isUintptr(Tu)) {
		return true
	}

	// ”V a slice，T是指向数组类型的指针，
	// 并且slice和数组类型具有相同的元素类型
	if s, _ := Vu.(*Slice); s != nil {
		if p, _ := Tu.(*Pointer); p != nil {
			if a, _ := under(p.Elem()).(*Array); a != nil {
				if Identical(s.Elem(), a.Elem()) {
					if check == nil || check.allowVersion(check.pkg, 1, 17) {
						return true
					}
					if cause != nil {
						*cause = "conversion of slices to array pointers requires go1.17 or later"
					}
				}
			}
		}
	}

	// 优化：如果我们没有类型参数，我们完成了
	if Vp == nil && Tp == nil {
		return false
	}

	errorf := func(format string, args ...any) {
		if check != nil && cause != nil {
			msg := check.sprintf(format, args...)
			if *cause != "" {
				msg += "\n\t" + *cause
			}
			*cause = msg
		}
	}

	// 带有特定类型项的泛型情况
	// （泛型操作数不能是常数，所以我们可以忽略x.val）
	switch {
	case Vp != nil && Tp != nil:
		x := *x // 不敲外部x 
		return Vp.is(func(V *term) bool {
			if V == nil {
				return false // 没有特定类型
			}
			x.typ = V.typ
			return Tp.is(func(T *term) bool {
				if T == nil {
					return false // 没有特定类型
				}
				if !x.convertibleTo(check, T.typ, cause) {
					errorf("cannot convert %s (in %s) to %s (in %s)", V.typ, Vp, T.typ, Tp)
					return false
				}
				return true
			})
		})
	case Vp != nil:
		x := *x // 不敲外部x 
		return Vp.is(func(V *term) bool {
			if V == nil {
				return false // 没有特定类型
			}
			x.typ = V.typ
			if !x.convertibleTo(check, T, cause) {
				errorf("cannot convert %s (in %s) to %s", V.typ, Vp, T)
				return false
			}
			return true
		})
	case Tp != nil:
		return Tp.is(func(T *term) bool {
			if T == nil {
			}
			if !x.convertibleTo(check, T.typ, cause) {
				errorf("cannot convert %s to %s (in %s)", x.typ, T.typ, Tp)
				return false
			}
			return true
		})
	}

	return false
}

func isUintptr(typ Type) bool {
	t, _ := under(typ).(*Basic)
	return t != nil && t.kind == Uintptr
}

func isUnsafePointer(typ Type) bool {
	t, _ := under(typ).(*Basic)
	return t != nil && t.kind == UnsafePointer
}

func isPointer(typ Type) bool {
	_, ok := under(typ).(*Pointer)
	return ok
}

func isBytesOrRunes(typ Type) bool {
	if s, _ := under(typ).(*Slice); s != nil {
		t, _ := under(s.elem).(*Basic)
		return t != nil && (t.kind == Byte || t.kind == Rune)
	}
	return false
}
